using System;
using System.Collections;
using TMPro;
using UnityEngine;

namespace Miao
{
    /// <summary>
    /// 打字机效果状态。
    /// </summary>
    public enum TypewriterState
    {
        /// <summary>
        /// 已完成输出。
        /// </summary>
        Completed,

        /// <summary>
        /// 正在输出。
        /// </summary>
        Outputting,

        /// <summary>
        /// 输出被中断。
        /// </summary>
        Interrupted
    }

    /// <summary>
    /// 用于TextMeshPro的打字机效果组件。
    /// 已知问题：
    ///   1. FadeRange大于0时，会强制将可见字符的透明度设为完全不透明。
    ///      要修复此问题，需要在开始输出字符前记录所有字符的原始透明度，并在执行字符淡化时代入记录的原始透明度进行计算。
    ///   2. 带有删除线、下划线、背景色等效果的文本不能正常显示。
    ///   3. 输出字符的过程中改变TextMeshPro组件的RectTransform参数，会导致文本显示异常。
    /// </summary>
    [RequireComponent(typeof(TMP_Text))]
    public class Typewriter : MonoBehaviour
    {
        /// <summary>
        /// 字符输出速度（字数/秒）。
        /// </summary>
        public byte OutputSpeed
        {
            get { return _outputSpeed; }
            set
            {
                _outputSpeed = value;
                CompleteOutput();
            }
        }

        /// <summary>
        /// 字符淡化范围（字数）。
        /// </summary>
        public byte FadeRange
        {
            get { return _fadeRange; }
            set
            {
                _fadeRange = value;
                CompleteOutput();
            }
        }

        public int writerPtr { get; set; }

        /// <summary>
        /// 打字机效果状态。
        /// </summary>
        public TypewriterState State { get; private set; } = TypewriterState.Completed;


        [Tooltip("字符输出速度（字数/秒）。")]
        [Range(1, 255)]
        [SerializeField]
        private byte _outputSpeed = 20;

        [Tooltip("字符淡化范围（字数）。")]
        [Range(0, 50)]
        [SerializeField]
        private byte _fadeRange = 10;

        /// <summary>
        /// TextMeshPro组件。
        /// </summary>
        private TMP_Text _textComponent;
        /// <summary>
        /// 用于输出字符的协程。
        /// </summary>
        //private RunningCoroutine runningCoroutine;
        private Coroutine runningCoroutine;

        /// <summary>
        /// 字符输出结束时的回调。
        /// </summary>
        private Action _outputEndCallback;


        /// <summary>
        /// 输出文字。
        /// </summary>
        /// <param name="text"></param>
        /// <param name="onOutputLineComplete"></param>
        public void PlayText(string text, Action onOutputLineComplete = null)
        {
            // 如果当前正在执行字符输出，重置执行。
            if (State == TypewriterState.Outputting)
            {
                StopCoroutine(runningCoroutine);
                State = TypewriterState.Interrupted;
                OnOutputEnd();
            }
            _outputEndCallback = onOutputLineComplete;
            // 开始新的字符输出协程
            if (FadeRange > 0)
            {
                runningCoroutine = StartCoroutine(OutputCharactersFadeing(text));
            }
            else
            {
                runningCoroutine = StartCoroutine(OutputCharactersNoFadeing(text));
            }
        }

        private void Awake()
        {
            _textComponent = GetComponent<TMP_Text>();
        }

        private void OnDisable()
        {
            if (Application.isEditor && isActiveAndEnabled)
            {
                CompleteOutput();
            }
        }
        WaitForFixedUpdate waitfixedUpdate = new WaitForFixedUpdate();
        /// <summary>
        /// 以不带淡入效果输出字符的协程。
        /// </summary>
        /// <param name="skipFirstCharacter"></param>
        /// <returns></returns>
        private IEnumerator OutputCharactersNoFadeing(string text)
        {
            State = TypewriterState.Outputting;
            _textComponent.text = text;
            _textComponent.maxVisibleCharacters = 0;
            _textComponent.ForceMeshUpdate();
            // 按时间逐个显示字符
            float timer = 0f;
            float interval = 1.0f / OutputSpeed;
            while (_textComponent.maxVisibleCharacters < _textComponent.textInfo.characterCount)
            {
                timer += Time.deltaTime;
                if (timer >= interval)
                {
                    timer = 0;
                    writerPtr++;
                    _textComponent.maxVisibleCharacters = writerPtr;
                }

                yield return waitfixedUpdate;
            }
            // 输出过程结束
            State = TypewriterState.Completed;
            OnOutputEnd();
        }


        /// <summary>
        /// 以带有淡入效果输出字符的协程。
        /// </summary>
        /// <returns></returns>
        private IEnumerator OutputCharactersFadeing(string text)
        {
            State = TypewriterState.Outputting;
            _textComponent.text = text;
            _textComponent.maxVisibleCharacters = 0;
            yield return waitfixedUpdate;// 等待一帧确保布局更新完成
            _textComponent.maxVisibleCharacters = _textComponent.textInfo.characterCount;
            _textComponent.ForceMeshUpdate();

            // 先将所有字符设置到透明状态
            for (int i = 0; i < _textComponent.textInfo.characterCount; i++)
            {
                SetCharacterAlpha(i, 0);
            }
            _textComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);

            // 按时间逐渐显示字符
            float timer = 0f;
            float interval = 1.0f / OutputSpeed;
            int headCharacterIndex = 0;
            while (State == TypewriterState.Outputting)
            {
                timer += Time.deltaTime;
                // 计算字符顶点颜色透明度
                bool isFadeCompleted = true;
                int tailIndex = headCharacterIndex - FadeRange + 1;
                for (int i = headCharacterIndex; i > -1 && i >= tailIndex; i--)
                {
                    // 不处理不可见字符，否则可能导致某些位置的字符闪烁
                    if (!_textComponent.textInfo.characterInfo[i].isVisible)
                    {
                        continue;
                    }

                    var step = headCharacterIndex - i;
                    var alpha = (byte)Mathf.Clamp((timer / interval + step) / FadeRange * 255, 0, 255);

                    isFadeCompleted &= alpha == 255;
                    SetCharacterAlpha(i, alpha);
                }
                _textComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);

                // 检查是否完成字符输出
                if (timer >= interval)
                {
                    if (headCharacterIndex < _textComponent.textInfo.characterCount - 1)
                    {
                        timer = 0;
                        headCharacterIndex++;
                        writerPtr++;
                    }
                    else if (isFadeCompleted)
                    {
                        State = TypewriterState.Completed;
                        OnOutputEnd();
                        yield break;
                    }
                }

                yield return waitfixedUpdate;
            }
        }

        /// <summary>
        /// 设置字符的顶点颜色Alpha值。
        /// </summary>
        /// <param name="index"></param>
        /// <param name="alpha"></param>
        private void SetCharacterAlpha(int index, byte alpha)
        {
            var materialIndex = _textComponent.textInfo.characterInfo[index].materialReferenceIndex;
            var vertexColors = _textComponent.textInfo.meshInfo[materialIndex].colors32;
            var vertexIndex = _textComponent.textInfo.characterInfo[index].vertexIndex;

            vertexColors[vertexIndex + 0].a = alpha;
            vertexColors[vertexIndex + 1].a = alpha;
            vertexColors[vertexIndex + 2].a = alpha;
            vertexColors[vertexIndex + 3].a = alpha;

            //newVertexColors[vertexIndex + 0] = (Color)newVertexColors[vertexIndex + 0] * ColorTint;
            //newVertexColors[vertexIndex + 1] = (Color)newVertexColors[vertexIndex + 1] * ColorTint;
            //newVertexColors[vertexIndex + 2] = (Color)newVertexColors[vertexIndex + 2] * ColorTint;
            //newVertexColors[vertexIndex + 3] = (Color)newVertexColors[vertexIndex + 3] * ColorTint;
        }
        /// 完成正在进行的打字机效果，将所有文字显示出来。
        /// </summary>
        public void CompleteOutput()
        {
            if (State == TypewriterState.Outputting)
            {
                State = TypewriterState.Completed;
                StopCoroutine(runningCoroutine);
                var textInfo = _textComponent.textInfo;
                writerPtr = writerPtr > 0 ? writerPtr : 0;
                for (int i = writerPtr - 1; i < textInfo.characterCount; i++)
                {
                    SetCharacterAlpha(writerPtr, 255);//更改当前的字体
                }
                _textComponent.maxVisibleCharacters = textInfo.characterCount;//更改最大可显示数
                _textComponent.ForceMeshUpdate();
                OnOutputEnd();
            }
        }
        /// <summary>
        /// 处理输出结束逻辑。
        /// </summary>
        /// <param name="isShowAllCharacters"></param>
        private void OnOutputEnd()
        {
            // 清理协程
            runningCoroutine = null;
            writerPtr = 0;
            // 触发输出完成回调，由于可能会出现在回调中再次调用Typewriter的情况，所以需要先赋空再调用
            if (_outputEndCallback != null)
            {
                var temp = _outputEndCallback;
                _outputEndCallback = null;
                temp.Invoke();
            }
        }
    }

#if UNITY_EDITOR
    [UnityEditor.CustomEditor(typeof(Typewriter))]
    class TypewriterEditor : UnityEditor.Editor
    {
        private Typewriter Target => (Typewriter)target;


        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            UnityEditor.EditorGUILayout.Space();
            UnityEditor.EditorGUI.BeginDisabledGroup(!Application.isPlaying || !Target.isActiveAndEnabled);
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Restart"))
            {
                Target.PlayText(Target.GetComponent<TMP_Text>().text);
            }
            if (GUILayout.Button("Complete"))
            {
                Target.CompleteOutput();
            }
            GUILayout.EndHorizontal();
            UnityEditor.EditorGUI.EndDisabledGroup();
        }
    }
#endif
}